﻿using System;
using System.Collections.Generic;
using System.IO;

using System.Threading;
using Microsoft.DirectX.DirectSound;
using Mp3Sharp;

namespace NGE
{
    public class Sound
    {
        Device dsounddev;

        SecondaryBuffer secondaryBuffer;
        WaveFormat waveFormat = new WaveFormat();
        BufferDescription bufferDescription;
        Mp3Stream mp3Stream;
        Thread fillControlthd;
        int wholeSize;
        int halfSize
        {
            get
            {
                return wholeSize / 2;
            }
        }
        byte[] buff = new byte[20000];

        public bool IsLoop = false;

        bool isPlaying = false;
        bool isNotPaused = true;

        public bool IsPlaying
        {
            get
            {
                if (isPlaying && isNotPaused)
                {
                    return true;
                }
                return false;
            }
        }

        int volume;
        public int Volume
        {
            set
            {
                volume = value;
                if (secondaryBuffer != null)
                {
                    secondaryBuffer.Volume = volume;
                }
            }
            get
            {
                return volume;
            }
        }

        public double Progress
        {
            get
            {
                if (mp3Stream == null)
                {
                    return 0;
                }
                else
                {
                    return (double)(mp3Stream.Position) / (double)(mp3Stream.Length);
                }
            }
            set
            {
                if (mp3Stream == null)
                {
                    return;
                }
                mp3Stream.Position = (long)(value * (double)mp3Stream.Length);
            }
        }

        public Sound(Game g)
        {
            dsounddev = new Microsoft.DirectX.DirectSound.Device();
            dsounddev.SetCooperativeLevel(g.GameControl, Microsoft.DirectX.DirectSound.CooperativeLevel.Normal);
            

            fillControlthd = new Thread(fillControl);
            fillControlthd.IsBackground = true;
            fillControlthd.Start();
        }

        bool pos, posb;
        //true: in fore | false: in back
        bool foreFilled, backFilled;
        string playingFileName = "";

        //public void Heartbeat()
        //{
        //    if (!IsPlaying || secondaryBuffer == null || secondaryBuffer.Disposed || mp3Stream == null)
        //    {

        //    }
        //    else
        //    {
        //        if (secondaryBuffer.PlayPosition < halfSize)
        //        {
        //            pos = true;
        //        }
        //        else
        //        {
        //            pos = false;
        //        }
        //        if (pos != posb)
        //        {
        //            if (pos)
        //            {
        //                backFilled = false;
        //            }
        //            else
        //            {
        //                foreFilled = false;
        //            }
        //        }
        //        posb = pos;
        //        if (mp3Stream.Position < mp3Stream.Length)
        //        {
        //            if (pos && !backFilled)
        //            {
        //                fillBack();
        //                backFilled = true;
        //            }
        //            else if (!pos && !foreFilled)
        //            {
        //                fillFore();
        //                foreFilled = true;
        //            }
        //        }
        //        else
        //        {
        //            //mp3Stream.Position = 0;
        //            secondaryBuffer.Stop();
        //            if (IsLoop)
        //            {
        //                play();
        //            }
        //            else
        //            {

        //                isPlaying = false;
        //            }
        //        }
        //    }
        //}

        public bool Load(string fileName)
        {
            if (System.IO.Path.GetExtension(fileName) == ".mp3")
            {
                return loadMP3(fileName, File.ReadAllBytes(fileName));
            }
            else if (System.IO.Path.GetExtension(fileName) == ".nsnd")
            {
                return loadMP3(fileName, ((NSound)Util.LoadObject(fileName)).DataByte);
            }
            return false;
        }
        bool loadMP3(string filename, byte[] data)
        {
            if (filename == null)
            {
                return false;
            }
            if (filename == string.Empty)
            {
                return false;
            }
            if (playingFileName == filename && mp3Stream != null)
            {
                return true;
            }
            if (isPlaying)
            {
                secondaryBuffer.Stop();
                isPlaying = false;
            }
            //if (secondaryBuffer == null)
            //{
            //    return;
            //}
            //if (secondaryBuffer.Disposed)
            //{
            //    return;
            //}
            playingFileName = filename;

            return loadMP3(data);
        }
        bool loadMP3(byte[] raw)
        {
            try
            {
                System.IO.MemoryStream ms = new System.IO.MemoryStream(raw);

                mp3Stream = new Mp3Stream(ms);
            }
            catch
            {
                return false;
            }

            mp3Stream.Read(buff, 0, 512);
            mp3Stream.Position = 0;

            waveFormat.BitsPerSample = 16;
            waveFormat.Channels = mp3Stream.ChannelCount;
            waveFormat.SamplesPerSecond = mp3Stream.Frequency;
            waveFormat.FormatTag = WaveFormatTag.Pcm;
            waveFormat.BlockAlign = (short)(waveFormat.Channels * (waveFormat.BitsPerSample / 8));
            waveFormat.AverageBytesPerSecond = waveFormat.SamplesPerSecond * waveFormat.BlockAlign;

            wholeSize = (int)(waveFormat.AverageBytesPerSecond * TimeSpan.FromSeconds(0.2).TotalSeconds);

            bufferDescription = new BufferDescription(waveFormat);
            bufferDescription.BufferBytes = wholeSize;
            bufferDescription.GlobalFocus = true;
            bufferDescription.ControlVolume = true;

            secondaryBuffer = new SecondaryBuffer(bufferDescription, dsounddev);
            secondaryBuffer.Volume = Volume;

            return true;

            #region useless code area
            //autoResetEvent = new System.Threading.AutoResetEvent(false);

            //notify = new Notify(secondaryBuffer);

            //System.Reflection.MethodInfo methodInfo;
            //methodInfo = typeof(SoundManager).GetMethod("fillBack");

            //bufferPositionNotify = new BufferPositionNotify[2];
            //bufferPositionNotify[0] = new BufferPositionNotify();
            //bufferPositionNotify[0].Offset = 0;
            //bufferPositionNotify[0].EventNotifyHandle = this.GetType().GetMethod("fillBack", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).MethodHandle.Value;
            //bufferPositionNotify[1] = new BufferPositionNotify();
            //bufferPositionNotify[1].Offset = halfSize;
            //bufferPositionNotify[1].EventNotifyHandle = this.GetType().GetMethod("fillFore", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).MethodHandle.Value;

            //bufferPositionNotify = new BufferPositionNotify[2];
            //bufferPositionNotify[0] = new BufferPositionNotify();
            //bufferPositionNotify[0].Offset = 0;
            //bufferPositionNotify[0].EventNotifyHandle = autoResetEvent.Handle;
            //bufferPositionNotify[1] = new BufferPositionNotify();
            //bufferPositionNotify[1].Offset = halfSize;
            //bufferPositionNotify[1].EventNotifyHandle = autoResetEvent.Handle;

            //notify.SetNotificationPositions(bufferPositionNotify);
            #endregion
        }
        public void Unload()
        {
            //if (bufferDescription != null)
            {
                bufferDescription.Dispose();
                secondaryBuffer.Dispose();


                mp3Stream.Dispose();
                mp3Stream = null;
            }
        }

        void fillFore()
        {
            if (mp3Stream == null)
            {
                return;
            }
            if (secondaryBuffer == null || secondaryBuffer.Disposed || mp3Stream == null)
            {
                return;
            }
            try
            {
                mp3Stream.Read(buff, 0, halfSize);
                secondaryBuffer.Write(0, new System.IO.MemoryStream(buff), halfSize, LockFlag.None);
            }
            catch { }
        }
        void fillBack()
        {
            if (mp3Stream == null)
            {
                return;
            }
            if (secondaryBuffer == null || secondaryBuffer.Disposed || mp3Stream == null)
            {
                return;
            }
            try
            {
                mp3Stream.Read(buff, 0, halfSize);
                secondaryBuffer.Write(halfSize, new System.IO.MemoryStream(buff), halfSize, LockFlag.None);
            }
            catch { }
        }
        void fillControl()
        {
            while (true)
            {
                if (!IsPlaying || secondaryBuffer == null || secondaryBuffer.Disposed || mp3Stream == null)
                {

                }
                else
                {
                    if (secondaryBuffer.PlayPosition < halfSize)
                    {
                        pos = true;
                    }
                    else
                    {
                        pos = false;
                    }
                    if (pos != posb)
                    {
                        if (pos)
                        {
                            backFilled = false;
                        }
                        else
                        {
                            foreFilled = false;
                        }
                    }
                    posb = pos;
                    if (mp3Stream.Position < mp3Stream.Length)
                    {
                        if (pos && !backFilled)
                        {
                            fillBack();
                            backFilled = true;
                        }
                        else if (!pos && !foreFilled)
                        {
                            fillFore();
                            foreFilled = true;
                        }
                    }
                    else
                    {
                        //mp3Stream.Position = 0;
                        secondaryBuffer.Stop();
                        if (IsLoop)
                        {
                            play();
                        }
                        else
                        {

                            isPlaying = false;
                        }
                    }
                }
                Thread.Sleep(10);
            }
        }
        //public void PlayMetalXAudio(string musicName) { PlayMetalXAudio(game.Audios.GetIndex(musicName)); }
        //public void PlayMetalXAudio(int i) { PlayMetalXAudio(new System.IO.MemoryStream(game.Audios[i].AudioData)); }

        void play()
        {
            if (mp3Stream == null)
            {
                return;
            }
            mp3Stream.Position = 0;
            try
            {
                mp3Stream.Read(buff, 0, halfSize);
            }
            catch { }
            pos = posb = true;
            foreFilled = true;
            backFilled = false;
            
            secondaryBuffer.Write(0, new System.IO.MemoryStream(buff), halfSize, LockFlag.None);
            secondaryBuffer.Play(0, BufferPlayFlags.Looping);
            isPlaying = true;
            isNotPaused = true;
        }

        public void Stop()
        {
            if (mp3Stream == null)
            {
                return;
            }
            if (secondaryBuffer == null)
            {
                return;
            }
            isPlaying = false;
            isNotPaused = true;
            secondaryBuffer.Stop();
            for (int i = 0; i < buff.Length; i++)
            {
                buff[i] = 0;
            }
            secondaryBuffer.Write(0, new System.IO.MemoryStream(buff), halfSize, LockFlag.None);
            secondaryBuffer.Write(halfSize, new System.IO.MemoryStream(buff), halfSize, LockFlag.None);
        }
        public void Pause()
        {
            isNotPaused = false;
            secondaryBuffer.Stop();
        }
        public void Play()
        {
            if (!isNotPaused)
            {
                isNotPaused = true;
                secondaryBuffer.Play(0, BufferPlayFlags.Looping);
            }
            else if (!isPlaying)
            {
                play();
            }
        }
    }
}
